﻿__author__ = 'Nick Shallery'

import uuid
import re
import time


class CmdOperator:
    __reg_with_key = re.compile(r"(@\([^\)]+\))")
    __reg_for_check_uuid = re.compile(r"[^0-9A-Fa-f]*")

    def __init__(self, project_name):
        self.__project_name = project_name
        self.__func_table = { "ProjName": self.__format_project_name,
                              "FileName": self.__format_project_name,
                              "UUID": self.__format_uuid,
                              "GUID": self.__format_uuid,
                              "CurrentTime": self.__format_time,
                              "LOLIX_DEF__ZERO_ITF_ID": self.__format_itf_id,
                              }

    def get_value(self, key):
        func = self.__get_func(key)
        if func is None:
            return key
        return func()

    def trans_lines(self, src_lines):
        rtv_lines = []
        modify = False
        for line in src_lines:
            new_line, lineModify = self.__trans_line(line)
            modify = modify or lineModify
            rtv_lines.append(new_line)
        return rtv_lines, modify

    def trans_line(self, src_line):
        return self.__trans_line(src_line)[0]

    def __trans_line(self, src_line):
        result = self.__reg_with_key.split(src_line)
        rtv = ""
        modify = False
        for i in range(len(result)):
            if len(result[i]) == 0:
                continue
            if result[i][0] != '@':
                rtv += result[i]
                continue
            modify = True
            tmp_str = result[i][2:len(result[i])-1]
            tmp_str = self.get_value(tmp_str)
            rtv += tmp_str

        new_rtv = rtv.rstrip()
        if '\n' in rtv or '\r' in rtv:
            new_rtv += '\n'

        modify = modify or new_rtv != rtv
        return new_rtv, modify

    def __get_func(self, func_name):
        func = self.__func_table.get(func_name)
        return func

    def __format_project_name(self):
        return self.__project_name

    @staticmethod
    def __format_uuid():
        uuid_dat = uuid.uuid4()
        return str.format("{{{}}}", str(uuid_dat))

    @classmethod
    def __format_itf_id(cls):
        uuid_dat = uuid.uuid4()
        uuid_str = str(uuid_dat)
        uuid_list = cls.__reg_for_check_uuid.split(uuid_str)
        all = "".join(uuid_list)
        return "0x{0}, 0x{1}, 0x{2}, 0x{3}".format(all[0:8], all[8:16], all[16:24], all[24:32])

    @staticmethod
    def __format_time():
        t = time.localtime(time.time())
        #t = datetime.time()
        return time.strftime("%Y-%m-%d %H:%M:%S", t)


def __get_file_lines(file_path):
    encoding_type = ['utf-8', 'utf-16', 'gb2312']
    for i in range(len(encoding_type)):
        try:
            fd = open(file_path, 'rt', encoding=encoding_type[i])
            with fd:
                src_list = list(fd)
                return src_list, encoding_type[i]
        except UnicodeError:
            print('skip file:', file_path, ' with ', encoding_type[i])
            continue
        except BaseException:
            print('skip file:', file_path, ' unknown error')
            break
    return [], Null


def __skip_unicode_signature(line):
    if len(line) > 0 and line[0] == '\ufeff'[0]:
        return line[1:]
    return line

def save_with_utf8_file(file_path, lines):
    if len(lines) == 0:
        return
    if len(lines[0]) == 0 or lines[0][0] != '\ufeff'[0]:
        lines[0] = "\ufeff{}".format(lines[0])
    with open(file_path, "wt", encoding="utf-8") as fs:
        fs.writelines(lines)


def __skip_black_line(lines):
    """
    skip all front space lines, include \\ufeff
    :param lines: the orgine lines
    :return: the index of the valid line(not space line)
    """
    if not lines:
        return 0
    if len(lines[0]) > 0 and lines[0][0] == '\ufeff'[0]:
        if not lines[0][1:].isspace():
            return 0

    i = 0
    for line in lines:
        if not line.isspace():
            break
        ++i
    return i


def append_file_header(lines):
    """
    追加文件头
    :param lines:当前文件的所有行
    :return:添加之后的所有文件行, 是否做了追加修改
    """
    no_space_line_index = __skip_black_line(lines)
    if no_space_line_index == len(lines):
        return lines, False
    r = re.compile(r"/\*\*\s*\\|@file")
    if r.search(lines[no_space_line_index]):
        return lines, False
    r = re.compile(r"/\*\*\s*$")
    if r.search(lines[no_space_line_index]):
        no_space_line_index += 1
    r = re.compile(r"\s*\*\s*\\|@file")
    if r.search(lines[no_space_line_index]):
        return lines, False
    new_lines = []
    new_lines.append("\ufeff/**\n")
    new_lines.append(" *	\\file		@(FileName)\n")
    new_lines.append(" *	\\date		(@(CurrentTime))/(@(CurrentTime))\n")
    new_lines.append(" *-----------------------------------------------------------------------------\n")
    new_lines.append(" *	\\brief\n")
    new_lines.append(" *	\\version	1.0.0.1\n")
    new_lines.append(" *	\\author		Nick Shallery	(nicknide@gmail.com)\n")
    new_lines.append(" *	\\copyright	YOU HAVE ALL LEFT WITH THIS CODE WHILE YOU KEEP COMMENT IN EVERY COPY.\n")
    new_lines.append(" *-----------------------------------------------------------------------------\n")
    new_lines.append("**/\n\n")

    old_start_line = __skip_unicode_signature(lines[no_space_line_index])
    new_lines.append(old_start_line)
    if no_space_line_index + 1 != len(lines):
        new_lines.extend(lines[no_space_line_index+1:])
    return new_lines, True


def trans_files(file_path, exlude = None):
    #import os.path
    import os
    if not os.path.isdir(file_path):
        return

    reg = re.compile(r'.*\.hpp$|.*\.h$|.*\.cpp$|.*\.cxx$|.*\.hxx$|.*\.inl$')
    for root, dirs, files in os.walk(file_path):
        if exlude and exlude.search(root + os.path.sep):
            #print('exlude dir:' + root)
            continue
        for file in files:
            res = reg.search(file)
            if res is None:
                #print('skip:' + file)
                continue
            full_path = os.path.join(root, file)
            if exlude and exlude.search(full_path):
                #print('exlude:' + full_path)
                continue
            lines, encodeType = __get_file_lines(full_path)
            need_save = encodeType != 'utf-8'
            lines, is_update = append_file_header(lines)
            need_save = need_save or is_update
            cmd_opt = CmdOperator(file)
            lines, is_update = cmd_opt.trans_lines(lines)
            need_save = need_save or is_update
            if need_save:
                save_with_utf8_file(full_path, lines)


def __autotest__():
    prj_name = "autotest_proj"
    cmdOpt = CmdOperator(prj_name)
    if cmdOpt.trans_line("@(ProjName)") != prj_name:
        print("ProjName {0} error : {1}".format(prj_name, cmdOpt.trans_line("@ProjName")))
        return False
    if cmdOpt.trans_line("@(ProjName):@(ProjName)") != "{0}:{0}".format(prj_name):
        print("@(ProjName):@(ProjName) error : {0}:{0} != {1}".format(prj_name, cmdOpt.trans_line("@(ProjName):@(ProjName)")))
        return False
    r = re.compile("0x[0-9a-f]{8}, 0x[0-9a-f]{8}, 0x[0-9a-f]{8}, 0x[0-9a-f]{8}")
    itf_id = cmdOpt.trans_line("@(LOLIX_DEF__ZERO_ITF_ID)")
    if not r.match(itf_id):
        print("format itf_id error:{}".format(itf_id))
        return False
    print("test ok")
    return True

if __name__ == "__main__":
    __autotest__()
    exit(0)
